﻿using RockBus.PerformanceCounters.Interfaces;
using RockBus.Queueing.Interfaces;
using System;
using System.Threading.Tasks.Dataflow;

namespace RockBus.Queueing.TplDataflow
{
    public class ActionBlockQueue<T> : IQueue<T>
    {
        public ActionBlockQueue(string name,
            Action<T> dequeueAction,
            ExecutionDataflowBlockOptions options,
            IPerformanceCounterFactory performanceCounterFactory)
        {
            this.Name = name;
            this.Options = options;
            this.DequeueAction = dequeueAction;
            this.PerformanceCounterFactory = performanceCounterFactory;
        }

        public string Name { get; private set; }

        public void Enqueue(T item)
        {
            IOperationCounter oc = null;
            try
            {
                oc = this.PerformanceCounterFactory.CreateOperationCounter(this.Name);
                oc.Start();
            }
            catch { }

            var entry = new Tuple<IOperationCounter, T>(oc, item);
            this.ActionBlock.Post(entry);
        }

        private Action<T> _dequeueAction;
        public Action<T> DequeueAction
        {
            private get { return this._dequeueAction; }
            set
            {
                this._dequeueAction = value;
                var old = this.ActionBlock;
                this.ActionBlock = new ActionBlock<Tuple<IOperationCounter, T>>((t) => { this.InternalDequeueMethod(t); }, this.Options);

                if (old != null)
                {
                    old.Complete();
                }
            }
        }

        private void InternalDequeueMethod(Tuple<IOperationCounter, T> entry)
        {
            try
            {
                if (entry.Item1 != null)
                {
                    entry.Item1.Complete();
                }
            }
            catch
            {
                try { entry.Item1.Abort(); } catch {}
            }

            this.DequeueAction(entry.Item2);
        }

        private IPerformanceCounterFactory PerformanceCounterFactory { get; set; }

        public int Count { get { return this.ActionBlock.InputCount; } }

        private ExecutionDataflowBlockOptions Options { get; set; }

        private ActionBlock<Tuple<IOperationCounter, T>> ActionBlock { get; set; }
    }
}